/**
 * 简易扫雷
 */
import styles from './index.less'

import React, { useState, useEffect } from 'react';
import { useRafInterval } from 'ahooks';
import { Toast } from 'antd-mobile';
import { GradeType, Options, MeshInfoType, MarkType } from './type'

const MineSweeper: React.FC = () => {
  const GradeInfoOptions = {
    primary: { // 初级
      grade: 'primary',
      row: 3,
      col: 3,
      mineNum: 1
    },
    middle: { // 中级
      grade: 'middle',
      row: 6,
      col: 6,
      mineNum: 3
    },
    difficult: { // 困难
      grade: 'difficult',
      row: 9,
      col: 9,
      mineNum: 10
    }
  }

  const [gradeInfo, setGradeInfo] = useState<Options>(GradeInfoOptions['primary']); //选择的等级，默认初级
  const [meshInfo, setMeshInfo] = useState<MeshInfoType[]>([]); // 每个格子的信息
  const [minePosition, setMinePositon] = useState<number[][]>([]); //雷的位置
  const [success, setSuccess] = useState<0 | 1 | null>(null); //是否成功 1成功，0失败
  const [mark, setMark] = useState<MarkType>({ status: 'waiting', markArr: [], clickNum: 0 }); //标记信息
  const [seconds, setSeconds] = useState<number>(0); //游戏计时多少秒
  const [interval, setInterval] = useState<number | undefined>(1000);//间隔时间，当取值 undefined 时会停止计时器
  const [history, setHistory] = useState<any>(null);//历史最高

  const timer = useRafInterval(() => {
    setSeconds(seconds + 1);
  }, interval);

  useEffect(() => {
    reStart();
    setHistory(localStorage.getItem(gradeInfo.grade));
  }, [gradeInfo])

  useEffect(() => {// 判断是否胜利
    if (minePosition.length == 0) {
      return
    }
    if ((mark.markArr.length) === minePosition.length) {
      let rightNum = 0
      minePosition.forEach(item => {
        mark.markArr.forEach(markItem => {
          if (item[0] === markItem[0] && item[1] === markItem[1]) {
            rightNum++
          }
        })
      })
      if (rightNum === minePosition.length) {
        setSuccess(1);
        setInterval(undefined);
        if (!history || (history && Number(history) > seconds)) {
          localStorage.setItem(gradeInfo.grade, JSON.stringify(seconds))
          setHistory(seconds);
        }
      }
    }
    if (gradeInfo.row * gradeInfo.col - mark.clickNum === minePosition.length && success !== 0) {
      setSuccess(1)
      setInterval(undefined);
      if (!history || (history && Number(history) > seconds)) {
        localStorage.setItem(gradeInfo.grade, JSON.stringify(seconds))
        setHistory(seconds);
      }
    }
  }, [mark])

  const reStart = () => {
    GenerateMine();
    GeneratingMesh();
    setSuccess(null);
    setMark(val => { return { ...val, status: 'waiting', markArr: [], clickNum: 0 } });
    setSeconds(0);
    setInterval(undefined);
  }
  const GenerateMine = () => {
    // 生成雷的位置
    let mineArr: number[][] = []
    let mineNum = gradeInfo.mineNum
    while (mineNum--) {
      let x = Math.floor(gradeInfo.row * Math.random());
      let y = Math.floor(gradeInfo.row * Math.random());
      let isRepeat = mineArr.some(item => item[0] === x && item[1] === y)

      if (isRepeat) {
        ++mineNum;
      } else {
        mineArr.push([x, y])
      }
    }
    setMinePositon(() => [...mineArr])
  }

  const GeneratingMesh = () => {
    // 生成格子
    let meshes: MeshInfoType[] = []
    for (let i = 0; i < gradeInfo.row; i++) {
      for (let j = 0; j < gradeInfo.col; j++) {
        meshes.push({ x: i, y: j, mineNum: null, isMine: false, mineFlag: false, isSigned: false, isClicked: false })
      }
    }
    setMeshInfo(() => [...meshes])
  }

  const clickMesh = (x: number, y: number, index: number) => {
    //第一次点击，开始计时
    if (seconds === 0) {
      setInterval(1000);
    }
    //已点击，将不执行后续操作
    if (meshInfo[index].isClicked && mark.status === 'waiting') return
    // 记录该格子已被点击
    let newMeshInfo = [...meshInfo]
    newMeshInfo[index].isClicked = true
    setMeshInfo(() => [...newMeshInfo])

    if (mark.status === 'marking') {//标记
      if (mark.markArr.length === minePosition.length) {
        Toast.show('标记已达上限，不能再标记了哟！')
        setMark((val) => { return { ...val, status: 'waiting', markArr: [...val.markArr, [x, y]] } })
        return
      }
      if (meshInfo[index].isSigned) {
        Toast.show('不能在此标记！')
        return
      }
      setMark((val) => { return { ...val, status: 'waiting', markArr: [...val.markArr, [x, y]] } })
      let meshArr = meshInfo
      meshArr[index].mineFlag = true
      setMeshInfo(() => [...meshArr])
      return
    } else if (mark.status === 'cancle') {//取消标记
      let cancleIndex: number
      let flag = false
      flag = mark.markArr.some((item, index) => {
        if (item[0] === x && item[1] === y) {
          cancleIndex = index
          return true
        }
      })
      if (!flag) {
        Toast.show('请点击标记过的！')
        return
      }
      setMark((val) => { return { ...val, status: 'waiting', markArr: [...mark.markArr.slice(0, cancleIndex), ...mark.markArr.slice(cancleIndex + 1)] } })
      let meshArr = meshInfo
      meshArr[index].mineFlag = false
      setMeshInfo(() => [...meshArr])
      return
    }
    if (meshInfo[index].mineFlag) {
      Toast.show('请先取消标记')
      return
    }
    // 查找周围8个格子有多少个雷
    // [x-1,y-1],[x-1,y],[x-1,y+1]
    // [x,y-1],[x,y],[x,y+1]
    // [x+1,y-1],[x+1,y],[x+1,y+1]
    let roundMesh = [[x - 1, y - 1], [x - 1, y], [x - 1, y + 1], [x, y - 1], [x, y + 1], [x + 1, y - 1], [x + 1, y], [x + 1, y + 1]]
    let mineNum = 0
    let isMine = false
    minePosition.forEach(item => {
      if (item[0] === x && item[1] === y) {//点中雷了
        isMine = true
        let meshArr = meshInfo
        meshArr[index].isMine = isMine
        setMeshInfo(() => [...meshArr])
        setSuccess(0)
        setInterval(undefined);
        return
      }
      roundMesh.forEach(mesh => {
        if (mesh[0] < 0 || mesh[1] < 0) {
          return
        } else if (mesh[0] === item[0] && mesh[1] === item[1]) {
          mineNum++
        }
      })
    })

    let meshArr = meshInfo
    meshArr[index].mineNum = mineNum
    meshArr[index].isMine = isMine
    meshArr[index].isSigned = true
    setMeshInfo(() => [...meshArr])

    setMark((val) => { return { ...val, clickNum: val.clickNum + 1 } })
  }

  const handleChange = (type: GradeType) => {
    if (gradeInfo.grade === type) {
      return
    }
    // 改变等级并重新开始
    setGradeInfo((val) => { return { ...val, ...GradeInfoOptions[type] } })
  }

  return (
    <div className={styles['mine-sweeper']}>
      {/* 选择困难程度区 */}
      <div className={styles['handle-content']}>
        <span className={`${styles['btn']} ${gradeInfo.grade === 'primary' ? styles['selected'] : ''}`} onClick={() => handleChange('primary')}>初级</span>
        <span className={`${styles['btn']} ${gradeInfo.grade === 'middle' ? styles['selected'] : ''}`} onClick={() => handleChange('middle')}>中级</span>
        <span className={`${styles['btn']} ${gradeInfo.grade === 'difficult' ? styles['selected'] : ''}`} onClick={() => handleChange('difficult')}>高级</span>
        <span className={styles['btn']} onClick={() => reStart()}>重新开始</span>
      </div>
      <div className={styles['game-wrap']}>
        {success === 1 || success === 0 ? <div className={styles['mask']}></div> : <></>}
        <div className={styles['desc']}>
          <p>哈哈，运气不好，第一脚就可能踩到雷哟;</p>
          <p>请不要点击你认为是雷的格子;</p>
          <p>点击后,格子中出现的数字，代表其周围有雷的格子数;</p>
        </div>
        <div className={styles['game-info']}>
          <p>这局有{gradeInfo.mineNum}个雷哟！有多少个雷，就能标记多少个格子！</p>
          <p>胜利条件：剩下没点的格子都是雷的时候 || 标记的都是雷;</p>
          <p className={styles['success']}>历史最高：{history ? history + '秒' : '无记录'}</p>
          <p className={styles['fail']}>计时：{seconds} 秒</p>
          {success === 1 ? <p className={styles['success']}>太厉害了，成功了！</p> : (success === 0 ? <p className={styles['fail']}>点中雷了！</p> : <p className={styles['waiting']}>还在路上，加油！</p>)}
        </div>
        <div>
          {
            mark.status === 'marking' ? <span className={`${styles['btn']} ${styles['selected']}`}>标记中</span> : <span className={styles['btn']} onClick={() => setMark((val => { return { ...val, status: 'marking' } }))}>点我开始标记</span>
          }
          {
            mark.status === 'cancle' ? <span className={`${styles['btn']} ${styles['selected']}`}>取消标记中</span> : (mark.markArr.length === 0 ? <></> : <span className={styles['btn']} onClick={() => setMark((val => { return { ...val, status: 'cancle' } }))}>点我取消标记</span>)
          }
        </div>
        {/* 扫雷区 */}
        <div className={`${styles['game-content']} ${styles[gradeInfo.grade]}`}>
          {
            meshInfo.map((mesh, index) => {
              return <span className={`${styles['mesh']} ${mesh.isMine ? styles['danger'] : ''} ${mesh.mineFlag ? styles['mesh-flag'] : ''}`} key={index} onClick={() => clickMesh(mesh.x, mesh.y, index)}>{mesh.isMine ? '嘭~' : meshInfo[index].mineNum}</span>
            })
          }
        </div>
      </div>

    </div>
  )
}

export default MineSweeper
